package jia.core;

import javax.vecmath.Vector2d;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Module {
	protected long id;
	protected String fileName;
	protected String name;
	protected Set<Zone> zones;
	protected Space[] spaces;
	protected int width, height;
	protected Set<EntryPoint> entryPoints;

	// ====================================================
	/**
	 * Creates a 16x16 module named "Unnamed"
	 */
	public Module() {
		name = "Unnamed";
		width = 16;
		height = 16;
		buildSpacesArray();
		zones = new HashSet<Zone>();
		entryPoints = new HashSet<EntryPoint>();
	}

	// ====================================================
	/**
	 * Creates a 16x16 module named as specified
	 *
	 * @param name
	 */
	public Module(String name) {
		this();
		this.name = name;
	}

	// ====================================================
	/**
	 * Creates a new module named and sized as specified
	 *
	 * @param name
	 * @param width
	 * @param height
	 */
	public Module(String name, int width, int height) {
		this.name = name;
		this.width = width;
		this.height = height;
		zones = new HashSet<Zone>();
		buildSpacesArray();
		entryPoints = new HashSet<EntryPoint>();
	}

	public Module(String fileName, boolean fromFile){
		this.setFileName(fileName);		
	}

	// ====================================================
	/**
	 * @param zone
	 */
	public void addZone(Zone zone) {
		zones.add(zone);
	}

	// ====================================================
	/**
	 * @param zone
	 */
	public void removeZone(Zone zone) {
		zones.remove(zone);
	}

	public Set<Zone> getZones() {
		return zones;
	}

	// ====================================================
	/**
	 * @return Returns the name.
	 */
	public String getName() {
		return name;
	}

	// ====================================================
	/**
	 * @param name The name to set.
	 */
	public void setName(String name) {
		this.name = name;
	}

	// ====================================================
	/**
	 * @return Returns the height.
	 */
	public int getHeight() {
		return height;
	}

	// ====================================================
	/**
	 * @return Returns the width.
	 */
	public int getWidth() {
		return width;
	}

	// ====================================================
	/**
	 * @param position
	 * @return
	 */
	public Space getSpace(Vector2d position) {
		return getSpace((int) position.x, (int) position.y);
	}

	// ====================================================
	/**
	 * @param x
	 * @param y
	 * @return
	 */
	public Space getSpace(int x, int y) {
		if (x >= width || y >= height || x < 0 || y < 0) {
			return null;
		}

		return spaces[x + (y * width)];
	}

	// ====================================================
	/**
	 * @param x
	 * @param y
	 * @param space
	 */
	public void setSpace(int x, int y, Space space) {
		if (x >= width || y >= height) {
			throw new IllegalArgumentException("Space indices x(" + x + ") and y("
					+ y + ") must be" + "between " + x
					+ " and " + y + ".");
		}

		spaces[x + (y * width)] = space;
	}

	// ====================================================
	/**
	 * @param position
	 * @param space
	 */
	public void setSpace(Vector2d position, Space space) {
		setSpace((int) position.x, (int) position.y, space);
	}

	// ====================================================
	/**
	 * @return
	 */
	public Set<EntryPoint> getEntryPoints() {
		return entryPoints;
	}

	// ====================================================
	/**
	 * @return
	 */
	public EntryPoint getDefaultEntryPoint() {
		Iterator<EntryPoint> i = entryPoints.iterator();
        for (Object entryPoint : entryPoints) {
            EntryPoint point = (EntryPoint) entryPoint;
            if (point.getName().equals(EntryPoint.DEFAULT_NAME)) return point;
        }

        return null;
	}

	// ====================================================
	/**
	 * @param entryPoint
	 */
	public void addEntryPoint(EntryPoint entryPoint) {
		entryPoints.add(entryPoint);
	}

	// ===============================================
	/**
	 * TODO: Make this method non-destructive
	 *
	 * @param height The height to set.
	 */
	public void setHeight(int height) {
		this.height = height;
		buildSpacesArray();
	}

	// ===============================================
	/**
	 * TODO: Make this method non-destructive
	 *
	 * @param width The width to set.
	 */
	public void setWidth(int width) {
		this.width = width;
		buildSpacesArray();
	}

	// ===============================================
	/**
	 * @param entryPoints The entryPoints to set.
	 */
	public void setEntryPoints(Set<EntryPoint> entryPoints) {
		this.entryPoints = entryPoints;
	}
	
	//===============================================
	/**
	 * TODO: We need a better way to ensure Hibernate creates
	 * an array of the full needed size
	 * <p/>
	 * Currently, this method creates an array with one too many
	 * indices, and adds an empty space to the final position.  This is so
	 * that Hibernate will create an array of the proper size on rehydration
	 * from the database.
	 */
	private void buildSpacesArray() {
		spaces = new Space[(width * height) + 1];
		spaces[width * height] = new Space();
	}

	public long getId(){
		return id;
	}

	public void setId(long id){
		this.id = id;
	}

	public String getFileName(){
		return fileName;
	}

	public void setFileName(String fileName){
		this.fileName = fileName;
	}
}
